<?php
/**
 *  Copyright 2009 Sven Wesley
 * 
 *  This API is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  The GNU GPL License is found at http://www.gnu.org/licenses/gpl.txt
 * 
 *  version 0.2
 *
 *  changelog:
 *    v0.2: Changes by Tiago Alves:
 *          - Added support for multiple arguments through <arg>, <arg1>, ...
 *          - Changed the error message format to include the error code
 *
 **/

$WS_URL = "http://localhost/api/soap/?wsdl";
$debug = false;

// Whether to return error messages in the original format (<error>msg</error>)
// or in the new format with the error code
$errorOldFormat = false;

function is_assoc($var) {
    return is_array($var) && array_keys($var) !== range(0, sizeof($var) - 1);
}

function resultToXml($responseName, $associativeArr, $xml) {
    $xml->startElement($responseName);
    arrayToXml($associativeArr, $xml);
    $xml->endElement();
}

function arrayToXml($array, $xml) {
    foreach ($array as $key => $value) {
        //Make some cleaning, no numericals or odd characters.
        if (is_numeric($key)) {
            $key = "element";
        }  //{ $key = "element_".$key; } //uncomment this to get element_x for arrays.
        $key = preg_replace("/[^a-zA-Z0-9_\.]/i", "", $key);
        //If this is a nested array, call myself.
        if (is_array($value)) {
            $xml->startElement($key);
            arrayToXml($value, $xml);
            $xml->endElement();
        } else {
            $xml->writeElement($key, $value);
        }
    }
}

function xml2array($xml) {
    $xmlArray = array();
    $regexp = "/<(\w+)\s*([^\/>]*)\s*(?:\/>|>(.*)<\/\s*\\1\s*>)/s";
    preg_match_all($regexp, $xml, $elements);
    foreach ($elements[1] as $ie => $element) {
        if (preg_match($regexp, $elements[3][$ie]))
            $xmlArray[$element] = xml2array($elements[3][$ie]);
        else if ($elements[3][$ie]) {
            $xmlArray[$element] = trim($elements[3][$ie]);
        }
    }
    return $xmlArray;
}

// Builds the argument array or value that must be sent
// as the third argument of the "call" SOAP request
function buildArgumentsArray($requestArray) {

    $result = array();
    // Write the default <arg> at index [0]
    if (isset($requestArray['arg'])) {
        $result[0] = $requestArray['arg'];
    }

    // Search for all <arg1>, <arg2>, ... tags and
    // write them at their respective index
    $found = false;
    $largestKey = PHP_INT_MIN;
    $keys = array_keys($requestArray);
    foreach ($keys as $key) {
        if (preg_match('/^arg(\d+)/', $key, $matches) > 0) {
            // Example: <arg3> will be writen at index [3]
            $index = (int) $matches[1];
            $largestKey = max($largestKey, $index);
            $result[$index] = $requestArray[$key];
            $found = true;
        }
    }
    if ($found || is_assoc($requestArray['arg'])) {
        // if there are any <arg1>, ... elements or
        // <arg> is associative, we
        // write the default <arg> at index [0]
        if (isset($requestArray['arg'])) {
            $result[0] = $requestArray['arg'];
        }
        // Set the indexes that might not have been set.
        // Ex: <arg2>, <arg4>
        for ($i = 0; $i <= $largestKey; $i++) {
            if (!isset($result[$i])) {
                $result[$i] = null;
            }
        }

        // Sort the array according to its keys because
        // Magento (the SOAP handler) takes the keys' order into account
        // and not the numeric value of its keys
        ksort($result);
    } else {
        $result = $requestArray['arg'];
    }

    return $result;
}

//FOR TEST
//$_POST["api_servicecall"] = "
// <request>
//    <call>login</call>
//    <user>webserviceuser</user>
//    <apikey>password123</apikey>
// </request>";

//$_POST["api_servicecall"] =
//"<request>
//   <call>product.list</call>
//   <session>1ee3c781ea762405725850c818f5fcb5</session>
//   <arg>
//     <name>
//       <like>SomeName%</like>
//     </name>
//   </arg>
// </request>";

//$_POST["api_servicecall"] =
//"<request>
//   <call>product.info</call>
//   <session>add-your-session-variable-here</session>
//   <arg>199</arg>
// </request>";

// Test "call" requests that receive multiple arguments such as
// category.assignedProducts
//$_POST["api_servicecall"] =
//"<request>
//   <call>category.assignedProducts</call>
//   <session>62f1384033493e5744ea14404238e633</session>
//   <arg>11</arg>
//   <arg1>2</arg1>
//   <arg2>
//      <someArgument>example</someArgument>
//   </arg2>
//</request>";

//$_POST["api_servicecall"] =
//"<request>
//   <call>multicall</call>
//   <session>add-your-session-variable-here</session>
//   <arg>
//     <call1>
//        <resource>directory_country.list</resource>
//     </call1>
//     <call2>
//        <resource>product.info</resource>
//        <arg>155</arg>
//     </call2>
//     <call3>
//        <resource>product.list</resource>
//        <arg>
//           <sku>
//             <like>DRO006</like>
//           </sku>
//        </arg>
//     </call3>
//   </arg>
// </request>";

//$_POST["api_servicecall"] = "
//<request>
//   <call>endsession</call>
//   <session>add-your-session-variable-here</session>
//</request>";

if (!array_key_exists("api_servicecall", $_POST)) {
    exit;
}

$ARG_ARRAY = xml2array($_POST["api_servicecall"]);
$API_CALL = $ARG_ARRAY["request"]["call"];
$soap = new SoapClient($WS_URL);
$resultArray = array();


if ($debug) {
    //can be helpful to see what the API really received.
    $fh = fopen('var/tmp/req.txt', 'a') or die("can't open file");
    fwrite($fh, $_POST["api_servicecall"]);
    fwrite($fh, "\n");
}

//Make the selected soap call
try {
    if ($API_CALL === "login") {
        $resultArray["login"] = $soap->login($ARG_ARRAY["request"]["user"], $ARG_ARRAY["request"]["apikey"]);
    } elseif ($API_CALL === "endsession") {
        $resultArray["endsession"] = $soap->endsession($ARG_ARRAY["request"]["session"]);
    } elseif ($API_CALL === "multicall") {
        //prepare the arguments list from the request array to a usable format.
        $requestArr = array();
        foreach ($ARG_ARRAY["request"]["arg"] as $mCall) {
            if (!array_key_exists("arg", $mCall)) {
                array_push($requestArr, array($mCall["resource"]));
            } elseif (array_key_exists("arg", $mCall) && is_assoc($mCall["arg"])) {
                array_push($requestArr, array($mCall["resource"], array($mCall["arg"])));
            } else {
                array_push($requestArr, array($mCall["resource"], $mCall["arg"]));
            }
        }
        $resultArray["multicall"] = $soap->multicall($ARG_ARRAY["request"]["session"], $requestArr);
    } else {
        $argument = buildArgumentsArray($ARG_ARRAY["request"]);
        $resultArray["call"] = $soap->call($ARG_ARRAY["request"]["session"], $API_CALL, $argument);
    }

    if ($debug) {
        fwrite($fh, "Result array: \n");
        fwrite($fh, print_r($resultArray, true));
        fwrite($fh, "\n--------------\n");
    }

    $responseRoot = "response";
} catch (Exception $e) {

    $resultArray = array();
    $resultArray['code'] = $e->faultcode;
    $resultArray['message'] = $e->faultstring;

    $responseRoot = "error";
}

// Return the response XML whether it is a
// successful response or not
$xml = new XMLWriter();
$xml->openURI("php://output");
$xml->startDocument("1.0");
$xml->setIndent(3);

if ($responseRoot == "error" && $errorOldFormat) {
    // Error reporting should be backwards compatible
    // return something like <error>error message</error>
    $xml->writeElement("error", $resultArray['message']);
}
else if (is_assoc($resultArray)) {
    resultToXml($responseRoot, $resultArray, $xml);
} else {
    foreach ($resultArray as $a) {
        resultToXml($responseRoot, $a, $xml);
    }
}

$xml->endDocument();
$xml->flush();

if ($debug) {
    fclose($fh);
}

?>